﻿using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using WH.Common.Config.Options;
using WH.Common.DbConfig;
using System;
using System.IO;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Threading.Tasks;
using System.Text;
using Microsoft.OpenApi.Models;
using System.Collections.Generic;
using WH.Common.Filter;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Microsoft.Extensions.FileProviders;
using WH.Services.Middleware;
using Microsoft.Extensions.Primitives;
using WH.Common.Config;
using RedisOptions = WH.Common.Config.Options.RedisOptions;
using WH.Repositories.CodeFirst;
using Microsoft.AspNetCore.Routing;
using WH.Common.Handler;
using Rw.Core.Common;
using Newtonsoft.Json.Serialization;
using WH.Common.SwaggerDoc.Extensions;
using Serilog;
using WH.Extensions.Setup.AutofacSetup;
using WH.Extensions.Setup;


await AppRunAsync();

var shell = Console.ReadLine();
while (shell != "exit")
{
}

async Task AppRunAsync()
{
    var builder = WebApplication.CreateBuilder(args);
    ConfigureServices(builder);

    var app = builder.Build();
    ConfigureApp(app);


    ChangeToken.OnChange(() => app.Configuration.GetReloadToken(), () =>
    {
        Console.WriteLine("Config changed.");
    });

    await CodeFirst(app);

    app.UseMvc(route =>
    {
        route.MapPost("/app/restart", async context =>
        {
            var token = context.Request.Headers["Authorization"].ToString();

            if (string.IsNullOrWhiteSpace(token))
            {
                await context.Response.WriteAsync("token不能为空");
                context.Response.StatusCode = 401;
                return;
            }

            //验证token，如果超过一个生命周期，则禁止刷新

            var _configurationCenter = new ConfigurationCenter(app.Configuration);

            var _jwtSettingOption = new JwtsettingOptions
            {
                Audience = _configurationCenter.JwtsettingOptions.Audience,
                Issuer = _configurationCenter.JwtsettingOptions.Issuer,
                Secret = _configurationCenter.JwtsettingOptions.Secret,
                Expires = _configurationCenter.JwtsettingOptions.Expires,
            };

            // 设置响应的内容类型为 "application/json"
            context.Response.ContentType = "application/json";

            var settings = new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            };


            var isValid = JwtTokenHandler.ValidateToken(token, _jwtSettingOption);
            if (!isValid)
            {
                var noAuthResp = new ResultResponse() { Code = 401, IsSucceed = false, Msg = "token无效" };

                await context.Response.WriteAsync(JsonConvert.SerializeObject(noAuthResp, settings));

                context.Response.StatusCode = 401;

                return;
            }


            _ = Task.Run(async () =>
             {
                 await app.StopAsync();
                 await app.DisposeAsync();

                 await Task.Delay(1000);

                 await AppRunAsync();
             });


            var resp = new ResultResponse() { Code = 200, IsSucceed = true, Msg = "重启成功。。。" };

            await context.Response.WriteAsync(JsonConvert.SerializeObject(resp, settings));

        });
    });

    await app.StartAsync();
}

void ConfigureServices(WebApplicationBuilder builder)
{
    // 1、配置host与容器
    builder.Host
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureContainer<ContainerBuilder>((context, builder) =>
        {
            var cacheOpttions = context.Configuration.GetSection("CacheOpttions").Get<CacheOpttions>();
            var redisOptions = context.Configuration.GetSection("RedisOptions").Get<RedisOptions>();

            var configuration = new ConfigurationCenter(context.Configuration);

            builder.RegisterInstance(configuration).As<IConfigurationCenter>().SingleInstance();

            builder.RegisterModule<AutofacModuleRegister>();

            builder.RegisterModule(new AutofacCachingSetup(configuration));

            builder.RegisterModule(new AutofacSqlSugarSetup(configuration));
            builder.RegisterModule<AutofacCodeFirstExtensionSetup>();
        })
        .ConfigureAppConfiguration((hostingContext, config) =>
        {
            config.Add(new DbConfigExtensions());
            config.AddJsonFile("appsettings.BasicPlatform.json", true, true);

        })
        .ConfigureLogging(loggingBuilder =>
        {
            //这里是清空所有日志提供程序,包括aspnetcore框架自带的控制台、调试窗口等
            loggingBuilder.ClearProviders();
        })
        //添加Serilog
        .UseSerilog((hostingContext, loggerConfiguration) =>
        {
            //从appsettings.json中读取配置
            loggerConfiguration.ReadFrom.Configuration(hostingContext.Configuration)
            .Enrich.FromLogContext()
            .WriteTo.Console()
            .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "log.txt"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: 31);
        });

    //设置接收文件长度的最大值。
    builder.Services.Configure<FormOptions>(x =>
    {
        x.ValueLengthLimit = int.MaxValue;
        x.MultipartBodyLengthLimit = int.MaxValue;
        x.MultipartHeadersLengthLimit = int.MaxValue;
    });

    builder.Services.Configure<KestrelServerOptions>(x => x.AllowSynchronousIO = true)
      .Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);

    builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();

    //services.AddSingleton<IConfigurationCenter, ConfigurationCenter>();

    //builder.Services.AddRegisters("WH.Repositories", "Repository", LifetimesEnum.Scoped);
    //builder.Services.AddRegisters("WH.Services", "Service", LifetimesEnum.Scoped);

    builder.Services.AddCors(c =>
    {
        c.AddPolicy("AllRequests", policy =>
        {
            policy.SetIsOriginAllowed(origin => true);
            policy.AllowAnyHeader();
            policy.AllowAnyMethod();
            policy.AllowCredentials(); //允许任何头
                                       //.AllowCredentials();//允许cookie
        });

    });

    var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtsettingOptions>() ?? new JwtsettingOptions();
    if (jwtSettings is not null)
    {
        //services.AddCacheSetup();

        //services.AddSqlSugarSetupWithConfigSetup();

        //services.AddCodeFirstExtensionHostedServiceSetup();

        //var jwtSettings = config.JwtsettingOptions ?? new MedicalIot.Common.Config.Options.JwtsettingOptions();

        //【认证】
        builder.Services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }).AddJwtBearer(o =>
        {
            o.Events = new JwtBearerEvents()
            {
                OnMessageReceived = context =>
                {
                    var token = context.Request.Headers["Authorization"].ToString().Replace("Bearer ", "");
                    try
                    {
                        if (string.IsNullOrWhiteSpace(token))
                        {
                            token = context.Request.Headers["access_token"].ToString();
                        }

                        if (string.IsNullOrWhiteSpace(token) && context.Request.Method.ToUpper().Equals("GET"))
                        {
                            token = context.Request.Query["access_token"].ToString();
                        }

                        if (string.IsNullOrWhiteSpace(token) && context.Request.Method.ToUpper().Equals("POST"))
                        {
                            token = context.Request.Form["access_token"].ToString();
                        }
                    }
                    catch (Exception)
                    {
                        token = string.Empty;
                    }
                    context.Token = token ?? string.Empty;

                    return Task.CompletedTask;
                }
            };

            o.TokenValidationParameters = new TokenValidationParameters
            {
                ValidAudience = jwtSettings.Audience,
                ValidIssuer = jwtSettings.Issuer,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Secret)),
                RequireExpirationTime = false,
                ClockSkew = TimeSpan.FromMinutes(10),
                ValidateLifetime = true
            };
        });

        var basePath = AppDomain.CurrentDomain.BaseDirectory;

        builder.Services.AddSwaggerDoc();
        builder.Services.AddSwaggerGen(c =>
       {
           c.SwaggerDoc("v1", new OpenApiInfo
           {
               Version = "v1.0.0",
               Title = "WH.WebAPI",
               Description = "WH WebApi 说明文档",
               Contact = new OpenApiContact
               {
                   Name = "",
                   Email = "",
                   Url = new Uri("http://www.www.www")
               }
           });

           //就是这里

           var xmlPath = Path.Combine(basePath, "WH.WebAPI.xml");//这个就是刚刚配置的xml文件名
           c.IncludeXmlComments(xmlPath, true);//默认的第二个参数是false，这个是controller的注释，记得修改

           var xmlModelPath = Path.Combine(basePath, "WH.Model.xml");//这个就是Model层的xml文件名
           c.IncludeXmlComments(xmlModelPath);

           var xmlModelPath1 = Path.Combine(basePath, "WH.Contracts.xml");//这个就是Contracts层的xml文件名
           c.IncludeXmlComments(xmlModelPath1);

           #region Token绑定到ConfigureServices

           // 发行人
           var security = new Dictionary<string, IEnumerable<string>> { { jwtSettings.Issuer, Array.Empty<string>() }, };

           //方案名称“WH.Api”可自定义，上下一致即可
           c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
           {
               Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}（注意两者之间是一个空格）\"",
               Name = "Authorization",//jwt默认的参数名称
               In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
               Type = SecuritySchemeType.ApiKey,
               BearerFormat = "JWT",
               Scheme = "Bearer"
           });

           c.AddSecurityRequirement(new OpenApiSecurityRequirement
                   {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference = new OpenApiReference {
                                Type = ReferenceType.SecurityScheme,
                                Id = "Bearer"
                            }
                        },
                        Array.Empty<string>()
                    }
              });

           #endregion Token绑定到ConfigureServices

           c.DocumentFilter<SwaggerEnumFilter>();
       });
        builder.Services.AddSwaggerGenNewtonsoftSupport();
    }



    //注入全局异常捕获
    builder.Services.AddMvc(option =>
    {
        option.Filters.Add<ValidateModelFilter>();
        option.Filters.Add<GlobalExceptionsFilter>();
        option.EnableEndpointRouting = false;
    })
        //.SetCompatibilityVersion(CompatibilityVersion.Version_3_0)
        .AddControllersAsServices().AddNewtonsoftJson
    (
       json =>
       {
           //修改属性名称的序列化方式，首字母小写，即驼峰样式
           //json.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
           //json字符串大小写原样输出
           //json.SerializerSettings.ContractResolver = new DefaultContractResolver();
           //日期类型默认格式化处理 
           json.SerializerSettings.DateFormatHandling = DateFormatHandling.MicrosoftDateFormat;
           json.SerializerSettings.DateFormatString = "yyyy-MM-dd HH:mm:ss";

           json.SerializerSettings.Converters.Add(new StringEnumConverter());
           json.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
       }
    );
    //var energyOptions = builder.Configuration.GetSection("Energy")?.Get<EnergyOptions>() ?? new EnergyOptions();

    //builder.Services.AddInfluxDbSetup(energyOptions);

    //builder.Services.AddJobSetup();

    //builder.Services.AddJobHostedServiceSetup();


}

void ConfigureApp(WebApplication app)
{

    #region Environment

    if (app.Environment.IsDevelopment())
    {
        // 在开发环境中，使用异常页面，这样可以暴露错误堆栈信息，所以不要放在生产环境。
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        // 在非开发环境中，使用HTTP严格安全传输(or HSTS) 对于保护web安全是非常重要的。
        // 强制实施 HTTPS 在 ASP.NET Core，配合 app.UseHttpsRedirection
        //app.UseHsts();
    }

    #endregion Environment

    #region HttpContext

    //Common.Web.HttpContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());

    #endregion HttpContext

    #region Swagger

    var swaggerConfig = app.Configuration.GetSection("Swagger").Get<SwaggerOptions>() ?? new SwaggerOptions();

    if (swaggerConfig.Enable)
    {
        app.UseSwagger();
        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "Rw.PrisonIot ApiDoc V1");
            // 将swagger首页，设置成我们自定义的页面，记得这个字符串的写法：解决方案名.index.html
            //c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("WH.WebAPI.index.html");//这里是配合MiniProfiler进行性能监控的，《文章：完美基于AOP的接口性能分析》，如果你不需要，可以暂时先注释掉，不影响大局。
            c.RoutePrefix = "swagger";
        });
    }
    #endregion Swagger

    app.UseMiddleware<ContextHandlerMiddleware>();

    app.UseMiddleware<OrgContextMiddleware>();

    app.UseMiddleware<OperationLogMiddleware>();

    app.UseWebSockets();

    app.UseCors("AllRequests");//将 CORS 中间件添加到 web 应用程序管线中, 以允许跨域请求。
                               //app.UseCors("LimitRequests");//将 CORS 中间件添加到 web 应用程序管线中, 以允许跨域请求。

    //// 跳转https
    ////app.UseHttpsRedirection();

    app.UseDefaultFiles();
    // 使用静态文件
    app.UseStaticFiles(new StaticFileOptions
    {
        OnPrepareResponse = ctx =>
        {
            if (ctx.Context.Request.Path.Value == "/index.html")
            {
                ctx.Context.Response.Headers.Append("Cache-Control", "no-store");
                ctx.Context.Response.Headers.Append("Pragma", "no-cache");
            }
        }
    });

    var pathAssets = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"assets");
    if (!Directory.Exists(pathAssets))
    {
        Directory.CreateDirectory(pathAssets);
    }

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(pathAssets),
        RequestPath = new PathString("/src"),
        OnPrepareResponse = ctx =>
        {
            ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=36000");
        }
    });


    app.UseStatusCodePages();//把错误码返回前台，比如是404

    app.UseRouting();

    #region Authen

    ////如果你想使用官方认证，必须在上边ConfigureService 中，配置JWT的认证服务 (.AddAuthentication 和 .AddJwtBearer 二者缺一不可)
    app.UseAuthentication();

    app.UseAuthorization();
    #endregion Authen

    app.UseMvc();

    //app.UseMvc();
    // app.UseEndpoints(endpoints =>
    //{
    //    endpoints.MapHub<PositionHub>("/PositionHub");
    //});
}

async Task CodeFirst(WebApplication app)
{
    var codeFirst = app.Services.CreateScope().ServiceProvider.GetRequiredService<CodeFirstExtension>();
    await codeFirst.InitDbTablesSeedData();
    // await codeFirst.GenerateSeedData();
}
